#include "ssm_state.h"

#include <gtest/gtest.h>

namespace {

typedef int Event;

const Event REENTER_STATE = 1;
const Event TRANSITION_TO_STATE_B = 2;


using namespace SimpleStateMachineLib;

class MyStateMachine: public StateMachine<MyStateMachine, Event>
{
public:
    class StateA: public StateType
    {
    public:
        int m_onEntryCallCount;
        int m_onExitCallCount;
        
        StateA()
            : m_onEntryCallCount(0),
            m_onExitCallCount(0)
        {
        }
        
        void onEntry()
        {
            ++m_onEntryCallCount;
        }
        
        void onExit()
        {
            ++m_onExitCallCount;
        }
        
        bool handleEvent(const EventType& ev)
        {
            if(ev == REENTER_STATE)
            {
                transition(*this);
            }
            else if(ev == TRANSITION_TO_STATE_B)
            {
                transition(machine()->m_StateB);
            }
            return false;
        }
    };
    
    StateA m_StateA;
    StateA m_StateB;
    
    MyStateMachine()
    {
        m_StateA.setStateMachine(this);
        m_StateB.setStateMachine(this);
        
        setInitialState(m_StateA);
    }
};


TEST(StateMachineTest, testCtorDoesNotEnterInitialState)
{
    MyStateMachine sm;
    EXPECT_EQ(0, sm.m_StateA.m_onEntryCallCount);
}

TEST(StateMachineTest, testStateMachineOnEntry_InitialStateOnEntryCalled)
{
    MyStateMachine sm;
    sm.onEntry();
    EXPECT_EQ(1, sm.m_StateA.m_onEntryCallCount);
}

TEST(StateMachineTest, StateAReenter_OnExitAndOnEntryCalled)
{
    MyStateMachine sm;
    sm.onEntry();
    sm.m_StateA.m_onEntryCallCount = 0;
    sm.handleEvent(REENTER_STATE);
    EXPECT_EQ(1, sm.m_StateA.m_onExitCallCount);
    EXPECT_EQ(1, sm.m_StateA.m_onExitCallCount);
}

TEST(StateMachineTest, StateTransition_OnExitSourceAndOnEntryDestinationCalled)
{
    MyStateMachine sm;
    sm.onEntry();
    
    sm.handleEvent(TRANSITION_TO_STATE_B);
    EXPECT_EQ(1, sm.m_StateA.m_onExitCallCount);
    EXPECT_EQ(1, sm.m_StateB.m_onEntryCallCount);
}


} // namespace

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
